#include <iconv.h>
#include "unicode.h"

size_t UnicodeLens(char *from)
{
	size_t sum;
	unsigned char c;
	int more;
	sum = 0;
	while((c = (*from++)))
	{
		if(c < 0x80)
			more = 0;
		else if(c < 0xe0)
			more = 1;
		else
			more = 2;

		from += more;
		sum++;
	}
	return sum;
}

size_t UnicodeToUtf8(char *to, unsigned short *from, size_t need, size_t *buffer_lens)
{
	unsigned short c;
	size_t len = 0;
	char *p = to;
	int count = 1;
	if(need == (unsigned int)-1)
		count = 0;
	while((c = *from++)!=0)
	{
		if(count && (need-- <= 0))
			break;
		if(c < 0x0080)
		{
			len ++;
			if(buffer_lens && (*buffer_lens < len))
			{
				len = (unsigned int)-1;
				break;
			}
			*p++ = c & 0x7f;
		}
		else if(!(c & 0xf800))
		{
			len += 2;
			if(buffer_lens && (*buffer_lens < len))
			{
				len = (unsigned int)-1;
				break;
			}
			*p++ = 0xc0 | (c >> 11);
			*p++ = 0x80 | (c & 0x3f);
		}
		else
		{
			len += 3;
			if(buffer_lens && (*buffer_lens < len))
			{
				len = (unsigned int)-1;
				break;
			}
			*p++ = 0xe0 | (c >> 12);
			*p++ = 0x80 | ((c >> 6) & 0x3f);
			*p++ = 0x80 | (c & 0x3f);
		}
	}
	if(buffer_lens && (*buffer_lens < len + 1))
		return (unsigned int)-1;
	*p = 0;
	return len;
}

size_t Utf8ToUnicode(unsigned short *to, char *from, size_t lens)
{
	size_t sum;
	unsigned short c;
	int more;
	int count = 1;
	if(lens == -1)
		count = 0;
	sum = 0;
	while((c = (unsigned short)(*from++)))
	{
		if(count && (lens-- <= 0))
			break;

		c &= 0xff;
		if(c < 0x80)
		{
			more = 0;
		}
		else if(c < 0x00e0)
		{
			more = 1;
			c &= 0x1f;
		}
		else
		{
			more = 2;
			c &= 0x0f;
		}

		sum++;
		lens -= more;

		while(more--)
		{
			if((*from & 0xc0) != 0x80)
			{
				sum = -1;
				break;
			}

		    c = (c << 6) + ((*from++) & 0x3f);
		}
		*to++ = c;
	}
	*to = 0;
	return sum;
}

int unicode_to_gb2312(char *outbuf, size_t outlen, char *inbuf, size_t inlen)
{
    int len, outsize;
    char *in, *out;
    iconv_t cd;

    if(!outbuf || !inbuf)
        return -1;
    cd = iconv_open("GB2312", "UNICODE");
    if(cd == 0)
        return -1;
    in = inbuf;
    out = outbuf;
    outsize = outlen;
    len = iconv(cd, &in, &inlen, &out, &outlen);
    iconv_close(cd);
    if(len == 0)
    {
        len = outsize - outlen;
    }
    return len;
}

int gb2312_to_unicode(char *outbuf, size_t outlen, char *inbuf, size_t inlen)
{
    int len, outsize;
    char *in, *out;
    iconv_t cd;

    if(!outbuf || !inbuf)
        return -1;
    cd = iconv_open("UNICODE", "GB2312");
    if(cd == 0)
        return -1;
    in = inbuf;
    out = outbuf;
    outsize = outlen;
    len = iconv(cd, &in, &inlen, &out, &outlen);
    iconv_close(cd);
    if(len == 0)
    {
        len = outsize - outlen;
    }
    return len;
}

